<?php
define('P_PAYMENT', 'plugin/boan_payment');
require_once 'SignData.php';

class AopClient extends boan_paymentBase{
    
    public $appId;
    
    public $rsaPrivateKeyFilePath;
    
    public $rsaPrivateKey;
    
    public $gatewayUrl = "https://openapi.alipay.com/gateway.do";
    
    public $format = "json";
    
    public $apiVersion = "1.0";
    
    public $postCharset = 'utf-8';
    
    public $alipayPublicKey = null;
    
    public $alipayrsaPublicKey;
    
    
    public $debugInfo = false;
    
    
    private $RESPONSE_SUFFIX = "_response";
    
    private $ERROR_RESPONSE = "error_response";
    
    private $SIGN_NODE_NAME = "sign";
    
    public $signType = "RSA2";
    
    public function generateSign($params, $signType = "RSA") {
        return $this->sign($this->getSignContent($params), $signType);
    }
    
    public function rsaSign($params, $signType = "RSA") {
        return $this->sign($this->getSignContent($params), $signType);
    }
    
    
    public function execute($request, $authToken = null, $appInfoAuthtoken = null) {
        
        $iv = null;
        
        if (!$this->checkEmpty($request->getApiVersion())) {
            $iv = $request->getApiVersion();
        } else {
            $iv = $this->apiVersion;
        }
        
        
        //组装系统参数
        $sysParams["app_id"] = $this->appId;
        $sysParams["version"] = $iv;
        $sysParams["format"] = $this->format;
        $sysParams["sign_type"] = $this->signType;
        $sysParams["method"] = $request->getApiMethodName();
        $sysParams["timestamp"] = date("Y-m-d H:i:s");
        $sysParams["auth_token"] = $authToken;
        $sysParams["alipay_sdk"] = $this->alipaySdkVersion;
        $sysParams["terminal_type"] = $request->getTerminalType();
        $sysParams["terminal_info"] = $request->getTerminalInfo();
        $sysParams["prod_code"] = $request->getProdCode();
        $sysParams["notify_url"] = $request->getNotifyUrl();
        $sysParams["charset"] = $this->postCharset;
        $sysParams["app_auth_token"] = $appInfoAuthtoken;
        
        
        //获取业务参数
        $apiParams = $request->getApiParas();
        
        //签名
        $sysParams["sign"] = $this->generateSign(array_merge($apiParams,$sysParams), $this->signType);
        //系统参数放入GET请求串
        $requestUrl = $this->gatewayUrl."?".http_build_query(array_merge($sysParams,$apiParams));
        
        //发起HTTP请求
        $resp = dfsockopen($requestUrl);
        //解析AOP返回结果
        $respWellFormed = false;
        
        // 将返回结果转换本地文件编码
        $r = $this->iconv($resp,'utf-8',CHARSET);
        
        $signData = null;
        
        if ("json" == $this->format) {
           
            
            $respObject = json_decode($r);
            if (null !== $respObject) {
                $respWellFormed = true;
                $signData = $this->parserJSONSignData($request, $resp, $respObject);
            }
        }
        
        //返回的HTTP文本不是标准JSON或者XML，记下错误日志
        if (false === $respWellFormed) {
            throw new Exception(lang(P_PAYMENT, 'alipay_err5'));
        }
        
        // 验签
        $this->checkResponseSign($request, $signData, $resp, $respObject);
        
        return $respObject;
    }

    
    
    public function pageExecute($request,$httpmethod = "POST") {
        
        $iv=null;
        
        if(!$this->checkEmpty($request->getApiVersion())){
            $iv=$request->getApiVersion();
        }else{
            $iv=$this->apiVersion;
        }
        
        //组装系统参数
        $sysParams["app_id"] = $this->appId;
        $sysParams["version"] = $iv;
        $sysParams["format"] = $this->format;
        $sysParams["sign_type"] = $this->signType;
        $sysParams["method"] = $request->getApiMethodName();
        $sysParams["timestamp"] = date("Y-m-d H:i:s");
        $sysParams["alipay_sdk"] = $this->alipaySdkVersion;
        $sysParams["terminal_type"] = $request->getTerminalType();
        $sysParams["terminal_info"] = $request->getTerminalInfo();
        $sysParams["prod_code"] = $request->getProdCode();
        $sysParams["notify_url"] = $request->getNotifyUrl();
        $sysParams["return_url"] = $request->getReturnUrl();
        $sysParams["charset"] = $this->postCharset;
       
        //获取业务参数
        $apiParams = $request->getApiParas();
        
        //print_r($apiParams);
        $totalParams = array_merge($apiParams, $sysParams);
        
        //待签名字符串
        $preSignStr = $this->getSignContent($totalParams);
        
        //签名
        $totalParams["sign"] = $this->generateSign($totalParams, $this->signType);
        
        if ("GET" == strtoupper($httpmethod)) {
           
            //value做urlencode
            $preString=$this->getSignContentUrlencode($totalParams);
           
            //拼接GET请求串
            $requestUrl = $this->gatewayUrl."?".$preString;
            
            return $requestUrl;
        } else {
            //拼接表单字符串
            return $this->buildRequestForm($totalParams);
        }
    }
    
    protected function buildRequestForm($para_temp) {
        
        $sHtml = "<html><head></head><body><form id='alipaysubmit' name='alipaysubmit' action='".$this->gatewayUrl."?charset=".trim($this->postCharset)."' method='POST'>";
        while (list ($key, $val) = each ($para_temp)) {
            if (false === $this->checkEmpty($val)) {
                $val = str_replace("'","&apos;",$val);
                $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
            }
        }
        
        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
        
        $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script></body></html>";
        
        return $sHtml;
    }
    
    
    //此方法对value做urlencode
    public function getSignContentUrlencode($params) {
        ksort($params);
        
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . urlencode($v);
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . urlencode($v);
                }
                $i++;
            }
        }
        
        unset ($k, $v);
       
        return $stringToBeSigned;
    }
    
    public function checkResponseSign($request, $signData, $resp, $respObject) {
        if (!$this->checkEmpty($this->alipayPublicKey) || !$this->checkEmpty($this->alipayrsaPublicKey)) {
            if ($signData == null || $this->checkEmpty($signData->sign) || $this->checkEmpty($signData->signSourceData)) {
                
                throw new Exception(" check sign Fail! The reason : signData is Empty");
            }
            // 获取结果sub_code
            $responseSubCode = $this->parserResponseSubCode($request, $resp, $respObject, $this->format);
            
            
            if (!$this->checkEmpty($responseSubCode) || ($this->checkEmpty($responseSubCode) && !$this->checkEmpty($signData->sign))) {
                
                $checkResult = $this->verify($signData->signSourceData, $signData->sign, $this->alipayPublicKey, $this->signType);
                
                if (!$checkResult) {
                    
                    if (strpos($signData->signSourceData, "\\/") > 0) {
                        
                        $signData->signSourceData = str_replace("\\/", "/", $signData->signSourceData);
                        
                        $checkResult = $this->verify($signData->signSourceData, $signData->sign, $this->alipayPublicKey, $this->signType);
                        
                        if (!$checkResult) {
                            throw new Exception("check sign Fail! [sign=" . $signData->sign . ", signSourceData=" . $signData->signSourceData . "]");
                        }
                        
                    } else {
                        
                        throw new Exception("check sign Fail! [sign=" . $signData->sign . ", signSourceData=" . $signData->signSourceData . "]");
                    }
                    
                }
            }
            
        }
    }
    
    
    
    public function rsaCheckV1($params, $rsaPublicKeyFilePath,$signType='RSA') {
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath,$signType);
    }
    
   
    
    function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') {
        
        if($this->checkEmpty($this->alipayPublicKey)){
            
            $pubKey= $this->alipayrsaPublicKey;
            $res = "-----BEGIN PUBLIC KEY-----\n" .
                wordwrap($pubKey, 64, "\n", true) .
                "\n-----END PUBLIC KEY-----";
        }else {
            //读取公钥文件
            $pubKey = file_get_contents($rsaPublicKeyFilePath);
            //转换为openssl格式密钥
            $res = openssl_get_publickey($pubKey);
        }
        
        ($res) or die('Alipay RSA public key error. Please check whether the public key file format is correct');
        
        
        if ("RSA2" == $signType) {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
        } else {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        }
        
        if(!$this->checkEmpty($this->alipayPublicKey)) {
            openssl_free_key($res);
        }
        
        return $result;
    }
    
    
    function parserResponseSubCode($request, $responseContent, $respObject, $format) {
        
        if ("json" == $format) {
            
            $apiName = $request->getApiMethodName();
            $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX;
            $errorNodeName = $this->ERROR_RESPONSE;
            
            $rootIndex = strpos($responseContent, $rootNodeName);
            $errorIndex = strpos($responseContent, $errorNodeName);
            
            if ($rootIndex > 0) {
                $rInnerObject = $respObject->$rootNodeName;
            } elseif ($errorIndex > 0) {
                
                $rInnerObject = $respObject->$errorNodeName;
            } else {
                return null;
            }
            
            if (isset($rInnerObject->sub_code)) {
                
                return $rInnerObject->sub_code;
            } else {
                
                return null;
            }
            
            
        } elseif ("xml" == $format) {
            
            return $respObject->sub_code;
            
        }
        
        
    }
    
    function checkEmpty($value) {
        if (!isset($value))
            return true;
            if ($value === null)
                return true;
                if (trim($value) === "")
                    return true;
                    return false;
    }
    
    function getSignContent($params) {
        ksort($params);
        $str = '';
        foreach ($params as $k=>$v){
            if($this->checkEmpty($v) === false){
                $str.= (empty($str) ? '' : '&').$k."=".(is_array($v) ? $this->json_encode($v) : $v);
            }
        }
        return $str;
    }
    
    function sign($data,$signType = 'RSA'){
        if(!$this->checkEmpty($this->rsaPrivateKey)){
            $priKey = $this->rsaPrivateKey;
            $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
        }
        ($res) or die('The format of the private key you are using is incorrect. Please check the RSA private key configuration');
        
        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }
        
        $sign = base64_encode($sign);
        if(empty($sign)){
            throw new Exception(lang(P_PAYMENT, 'alipay_err6'));
        }
        return $sign;
    }
    
    function parserJSONSignData($request, $responseContent, $responseJSON) {
        
        $signData = new SignData();
        
        $signData->sign = $this->parserJSONSign($responseJSON);
        $signData->signSourceData = $this->parserJSONSignSource($request, $responseContent);
        
        
        return $signData;
        
    }
    
    function parserJSONSignSource($request, $responseContent) {
        
        $apiName = $request->getApiMethodName();
        $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX;
        
        $rootIndex = strpos($responseContent, $rootNodeName);
        $errorIndex = strpos($responseContent, $this->ERROR_RESPONSE);
        
        
        if ($rootIndex > 0) {
            
            return $this->parserJSONSource($responseContent, $rootNodeName, $rootIndex);
        } else if ($errorIndex > 0) {
            
            return $this->parserJSONSource($responseContent, $this->ERROR_RESPONSE, $errorIndex);
        } else {
            
            return null;
        }
        
    }
    
    function parserJSONSource($responseContent, $nodeName, $nodeIndex) {
        $signDataStartIndex = $nodeIndex + strlen($nodeName) + 2;
        $signIndex = strpos($responseContent, "\"" . $this->SIGN_NODE_NAME . "\"");
        // 签名前-逗号
        $signDataEndIndex = $signIndex - 1;
        $indexLen = $signDataEndIndex - $signDataStartIndex;
        if ($indexLen < 0) {
            
            return null;
        }
        
        return substr($responseContent, $signDataStartIndex, $indexLen);
        
    }
    
    function parserJSONSign($responseJSon) {
        
        return $responseJSon->sign;
    }
    
    
    function getApiResponseName($request){
        return str_replace('.', '_', $request->getApiMethodName()).$this->RESPONSE_SUFFIX;
    }
}